package net.gaox.search.search.impl;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import net.gaox.search.index.DataTable;
import net.gaox.search.index.enums.CommonStatus;
import net.gaox.search.index.unit.adunit.AdUnitIndex;
import net.gaox.search.index.unit.adunit.AdUnitObject;
import net.gaox.search.index.unit.creative.CreativeIndex;
import net.gaox.search.index.unit.creative.CreativeObject;
import net.gaox.search.index.unit.creativeunit.CreativeUnitIndex;
import net.gaox.search.index.unit.district.UnitDistrictIndex;
import net.gaox.search.index.unit.interest.UnitItIndex;
import net.gaox.search.index.unit.keyword.UnitKeywordIndex;
import net.gaox.search.search.ISearch;
import net.gaox.search.search.vo.SearchRequest;
import net.gaox.search.search.vo.SearchResponse;
import net.gaox.search.search.vo.feature.DistrictFeature;
import net.gaox.search.search.vo.feature.FeatureRelation;
import net.gaox.search.search.vo.feature.ItFeature;
import net.gaox.search.search.vo.feature.KeywordFeature;
import net.gaox.search.search.vo.media.AdSlot;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import java.util.*;


/**
 * <p> 检索服务实现类 </p>
 *
 * @author gaox·Eric
 * @site gaox.net
 * @date 2019/12/20 16:18
 */
@Slf4j
@Service
public class SearchImpl implements ISearch {

    @Override
    public SearchResponse fetchAds(SearchRequest request) {

        // 请求的广告位信息
        List<AdSlot> adSlots = request.getRequestInfo().getAdSlots();

        // 三个 Feature
        KeywordFeature keywordFeature = request.getFeatureInfo().getKeywordFeature();
        DistrictFeature districtFeature = request.getFeatureInfo().getDistrictFeature();
        ItFeature itFeature = request.getFeatureInfo().getItFeature();
        // 匹配关系
        FeatureRelation relation = request.getFeatureInfo().getRelation();

        // 构造响应对象
        SearchResponse response = new SearchResponse();
        Map<String, List<SearchResponse.Creative>> adSlot2Ads = response.getAdSlot2Ads();

        for (AdSlot adSlot : adSlots) {
            Set<Long> targetUnitIdSet;

            // 根据流量类型获取初始 AdUnit
            Set<Long> adUnitIdSet = DataTable.of(AdUnitIndex.class).match(adSlot.getPositionType());

            if (relation == FeatureRelation.AND) {
                filterKeywordFeature(adUnitIdSet, keywordFeature);
                filterDistrictFeature(adUnitIdSet, districtFeature);
                filterItTagFeature(adUnitIdSet, itFeature);

                targetUnitIdSet = adUnitIdSet;
            } else {
                targetUnitIdSet = getORRelationUnitIds(adUnitIdSet, keywordFeature, districtFeature, itFeature);
            }

            // 根据 推广单元id 获取 推广单元列表
            List<AdUnitObject> unitObjects = DataTable.of(AdUnitIndex.class).fetch(targetUnitIdSet);
            filterAdUnitAndPlanStatus(unitObjects, CommonStatus.VALID);

            // 根据 推广单元 获取 创意列表
            List<Long> adIds = DataTable.of(CreativeUnitIndex.class).selectAds(unitObjects);
            List<CreativeObject> creatives = DataTable.of(CreativeIndex.class).fetch(adIds);

            // 通过 AdSlot 实现对 CreativeObject 的过滤
            filterCreativeByAdSlot(creatives, adSlot.getWidth(), adSlot.getHeight(), adSlot.getType());
            // 放入结果
            adSlot2Ads.put(adSlot.getAdSlotCode(), buildCreativeResponse(creatives));
        }

        log.info("fetchAds: {}-{}", JSON.toJSONString(request), JSON.toJSONString(response));
        return response;
    }

    /**
     * 匹配关系为或 OR 时，获取所有匹配到的推广单元
     *
     * @param adUnitIdSet     广告单元ids
     * @param keywordFeature  关键词
     * @param districtFeature 地域
     * @param itFeature       兴趣
     * @return
     */
    private Set<Long> getORRelationUnitIds(Set<Long> adUnitIdSet, KeywordFeature keywordFeature,
                                           DistrictFeature districtFeature, ItFeature itFeature) {

        if (CollectionUtils.isEmpty(adUnitIdSet)) {
            return Collections.emptySet();
        }

        Set<Long> keywordUnitIdSet = new HashSet<>(adUnitIdSet);
        Set<Long> districtUnitIdSet = new HashSet<>(adUnitIdSet);
        Set<Long> itUnitIdSet = new HashSet<>(adUnitIdSet);

        filterKeywordFeature(keywordUnitIdSet, keywordFeature);
        filterDistrictFeature(districtUnitIdSet, districtFeature);
        filterItTagFeature(itUnitIdSet, itFeature);
        // 获取3个集合的并集
        return new HashSet<>(CollectionUtils.union(CollectionUtils.union(keywordUnitIdSet, districtUnitIdSet), itUnitIdSet));
    }

    /**
     * 通过 关键词 过滤
     *
     * @param adUnitIds      所有广告单元列表
     * @param keywordFeature 搜索关键词
     */
    private void filterKeywordFeature(Collection<Long> adUnitIds, KeywordFeature keywordFeature) {
        if (CollectionUtils.isEmpty(adUnitIds)) {
            return;
        }

        List<String> keywords = keywordFeature.getKeywords();
        if (CollectionUtils.isNotEmpty(keywords)) {
            CollectionUtils.filter(adUnitIds, adUnitId -> DataTable.of(UnitKeywordIndex.class).match(adUnitId, keywords));
        }
    }

    /**
     * 过 区域 过滤
     *
     * @param adUnitIds       所有广告单元列表
     * @param districtFeature 搜索关键词
     */
    private void filterDistrictFeature(Collection<Long> adUnitIds, DistrictFeature districtFeature) {
        if (CollectionUtils.isEmpty(adUnitIds)) {
            return;
        }

        List<DistrictFeature.ProvinceAndCity> districts = districtFeature.getDistricts();
        if (CollectionUtils.isNotEmpty(districts)) {
            CollectionUtils.filter(adUnitIds, adUnitId -> DataTable.of(UnitDistrictIndex.class).match(adUnitId, districts));
        }
    }

    /**
     * 过 兴趣 过滤
     *
     * @param adUnitIds 所有广告单元列表
     * @param itFeature 搜索关键词
     */
    private void filterItTagFeature(Collection<Long> adUnitIds, ItFeature itFeature) {
        if (CollectionUtils.isEmpty(adUnitIds)) {
            return;
        }

        List<String> its = itFeature.getIts();
        if (CollectionUtils.isNotEmpty(its)) {
            CollectionUtils.filter(adUnitIds, adUnitId -> DataTable.of(UnitItIndex.class).match(adUnitId, its));
        }
    }

    /**
     * 根据状态 过滤广告单元状态和广告单元对象状态
     *
     * @param unitObjects 广告单元
     * @param status      状态
     */
    private void filterAdUnitAndPlanStatus(List<AdUnitObject> unitObjects, CommonStatus status) {
        if (CollectionUtils.isEmpty(unitObjects)) {
            return;
        }

        CollectionUtils.filter(unitObjects, object ->
                object.getUnitStatus().equals(status.getStatus())
                        && object.getAdPlanObject().getPlanStatus().equals(status.getStatus()));
    }

    /**
     * 根据广告位条件 过滤
     *
     * @param creatives 列表
     * @param width     宽度
     * @param height    高度
     * @param type      类型
     */
    private void filterCreativeByAdSlot(List<CreativeObject> creatives, Integer width,
                                        Integer height, List<Integer> type) {
        if (CollectionUtils.isEmpty(creatives)) {
            return;
        }

        CollectionUtils.filter(creatives, creative ->
                creative.getAuditStatus().equals(CommonStatus.VALID.getStatus())
                        && creative.getWidth().equals(width)
                        && creative.getHeight().equals(height)
                        && type.contains(creative.getType()));
    }

    /**
     * 构建返回对象
     *
     * @param creatives 创意列表
     * @return 返回对象
     */
    private List<SearchResponse.Creative> buildCreativeResponse(List<CreativeObject> creatives) {
        if (CollectionUtils.isEmpty(creatives)) {
            return Collections.emptyList();
        }
        // 随机获取一个创意对象
        CreativeObject randomObject = creatives.get(Math.abs(new Random().nextInt()) % creatives.size());
        return Collections.singletonList(SearchResponse.convert(randomObject));
    }

}
